<?php
/*
 * This class maps urls => requests and requests => urls
 */
class urlmapper
{
    private $request_vars  = array();
    private $mapping_rules = array();

    public function __construct($rules)
    {
        if (empty($rules))
        {
            throw new Exception('sbook/urlmapper: no rules found!');
        }
        foreach($rules as $rule)
        {
            $this->add_rule($rule['url'],$rule['map']);
        }
    }

    public function parse($url = '', $method = 'GET')
    {
        foreach ($this->mapping_rules as $rule)
        {
            if ($request = $this->match_rule($url, $rule, $method))
            {
                $this->request_vars['method'] = $method;
                return $this->process_vars();
            }
        } 
        return new request();
    }

    private function process_vars()
    {
        foreach($this->request_vars as $key=>$value)
        {
            switch ($key) {
            case 'method': 
            case 'app':
            case 'controller':
            case 'action':
                $$key = $value;
                break;
            default:
                $params[$key] = $value;
            }
        }
        return new request($app,$controller,$action,$params,$method);
    }

    private function match_rule($url, $rule)
    {
        $rule_parts = split('/',$rule['url']);
        $url_parts  = split('/',$url);

        $this->request_vars = $rule['map'];

        foreach ($rule_parts as $idx=>$part)
        {
            // match wildcard (and stop processing)
            if ($part == '*')
            {
                foreach(array_slice($url_parts,$idx) as $pos=>$param)
                    $this->request_vars['param'.$pos] = $param;
                break;
            }

            // regular matches
            $match = $this->match_parts($url_parts[$idx], $part);

            if (is_null($match) or $match == false)
                return false;

            if (is_array($match)) 
            {
                $this->request_vars[$match['key']] = $match['value'];
            }
        }
        return true;
    }

    private function match_parts($url_part,$rule_part)
    {
        if ($this->is_rule_var($rule_part))
            return $this->match_var($url_part,$rule_part);
        else
            return ($url_part == $rule_part);
    }

    private function is_rule_var($rule_part)
    {
        return ($rule_part[0] == ':'); // || $rule_part == '*');
    }

    private function match_var($url_part, $rule_part)
    {
        if ($rule_part[0] == ':')
        {
            preg_match("/:(?P<var>\w+)(?:\{(?P<pattern>.+)\})?/",$rule_part,$match);
            
            if (!isset($match['pattern']))
            {
                $match['pattern'] = '\w+';
            }

            $test = preg_match("/^".$match['pattern']."$/",$url_part);

            if ($test == 0)
            {
                return false;
            }
            else
            {
                return array('key' => $match['var'], 'value' => $url_part);
            }
        }
        elseif ($rule_part == '*')
        {
            return true;
        }
    }

    public function add_rule($url,$map)
    {
        // TODO: verify input
        $this->mapping_rules[] = array('url'=>$url,'map'=>$map);
    }

    public function rules_count()
    {
        return count($this->mapping_rules);
    }

    public function get_rule($index)
    {

    }
}
?>
